//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

data.cpp

Abstract:


Notes:


--*/


#include "precomp.h"

//
// Bearer service names
//
static const DWORD g_rgdwBearerSvcNames[] =
{
    -1,     // RIL_BSVCNAME_UNKNOWN
    0,      // RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM
    1,      // RIL_BSVCNAME_DATACIRCUIT_SYNC_UDI_MODEM
    2,      // RIL_BSVCNAME_PADACCESS_ASYNC_UDI
    3,      // RIL_BSVCNAME_PACKETACCESS_SYNC_UDI
    4,      // RIL_BSVCNAME_DATACIRCUIT_ASYNC_RDI
    5,      // RIL_BSVCNAME_DATACIRCUIT_SYNC_RDI
    6,      // RIL_BSVCNAME_PADACCESS_ASYNC_RDI
    7,      // RIL_BSVCNAME_PACKETACCESS_SYNC_RDI
};
#define NUM_BEARERSVCNAMES  (sizeof(g_rgdwBearerSvcNames) / sizeof(DWORD))


//
// Bearer service connection elements
//
static const DWORD g_rgdwBearerSvcConnElems[] =
{
    -1,     // RIL_BSVCCE_UNKNOWN
    0,      // RIL_BSVCCE_TRANSPARENT
    1,      // RIL_BSVCCE_NONTRANSPARENT
    2,      // RIL_BSVCCE_BOTH_TRANSPARENT
    3,      // RIL_BSVCCE_BOTH_NONTRANSPARENT
};
#define NUM_BEARERSVCCONNELEMS  (sizeof(g_rgdwBearerSvcConnElems) / sizeof(DWORD))


static const LISTVALUEMAP g_DataCompDirection[] =
{
    {0, RIL_DATACOMPDIR_NONE},
    {1, RIL_DATACOMPDIR_TRANSMIT},
    {2, RIL_DATACOMPDIR_RECEIVE},
    {3, RIL_DATACOMPDIR_BOTH},
};
#define NUM_DATACOMPDIRECTION (sizeof(g_DataCompDirection) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_DataCompNegotiation[] =
{
    {0, RIL_DATACOMP_OPTIONAL},
    {1, RIL_DATACOMP_REQUIRED},
};
#define NUM_DATACOMPNEGOTIATION (sizeof(g_DataCompNegotiation) / sizeof(LISTVALUEMAP))



//
// Error correction values: Original Request
//
static const LISTVALUEMAP g_ErrorCorrectionOrigRqst[] =
{
    {0, RIL_ECMODE_DIRECT},
    {1, RIL_ECMODE_BUFFERED},
    {2, RIL_ECMODE_NODETECT},
    {3, RIL_ECMODE_DETECT},
    {4, RIL_ECMODE_ALTERNATIVE},
};
#define NUM_ERRORCORRECTIONORIGRQST (sizeof(g_ErrorCorrectionOrigRqst) / sizeof(LISTVALUEMAP))

//
// Error correction values: Original Fallback
//
static const LISTVALUEMAP g_ErrorCorrectionOrigFbk[] =
{
    {0, RIL_ECMODE_OPTIONAL_USEBUFFERED},
    {1, RIL_ECMODE_OPTIONAL_USEDIRECT},
    {2, RIL_ECMODE_REQUIRED},
    {3, RIL_ECMODE_REQUIRED_LAPMONLY},
    {4, RIL_ECMODE_REQUIRED_ALTERNATIVEONLY},
};
#define NUM_ERRORCORRECTIONORIGFBK (sizeof(g_ErrorCorrectionOrigFbk) / sizeof(LISTVALUEMAP))

//
// Error correction values: Original Fallback
//
static const LISTVALUEMAP g_ErrorCorrectionAnsFbk[] =
{
    {0, RIL_ECMODE_DIRECT},
    {1, RIL_ECMODE_BUFFERED},
    {2, RIL_ECMODE_OPTIONAL_USEBUFFERED},
    {3, RIL_ECMODE_OPTIONAL_USEDIRECT},
    {4, RIL_ECMODE_REQUIRED},
    {5, RIL_ECMODE_REQUIRED_LAPMONLY},
    {6, RIL_ECMODE_REQUIRED_ALTERNATIVEONLY},
};
#define NUM_ERRORCORRECTIONANSFBK (sizeof(g_ErrorCorrectionAnsFbk) / sizeof(LISTVALUEMAP))

#ifdef APP_CSD_SETTING
const LPCTSTR g_tszRegKeyCsdSetting = TEXT("ControlPanel\\CSDSetting");
#endif
//
//
//
static HRESULT ParseGetHSCSDOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetHSCSDOptions);
    UINT nValue;
    UINT i;
    RILHSCSDINFO* prhscsdi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prhscsdi = (RILHSCSDINFO*)AllocBlob(sizeof(RILHSCSDINFO));
    if (!prhscsdi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prhscsdi, 0x00, sizeof(RILHSCSDINFO));
    prhscsdi->cbSize = sizeof(RILHSCSDINFO);

#ifndef EMP_DRIVER
    // EMP does not support +CHST
    // Parse "<prefix>+CHST: <rx_timeslots>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CHST: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prhscsdi->dwTranspRxTimeslots = nValue;
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_TRANSPRXTIMESLOTS;

    // Parse ",<codings><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Set all required channel coding bits
    if (!nValue)
    {
        prhscsdi->dwTranspChannelCodings = RIL_HSCSDCODING_ALL;
    }
    else
    {
        for (i = 0 ; i < NUM_HSCSDCODINGS; i++)
        {
            if (nValue & (0x01 << i))
            {
                prhscsdi->dwTranspChannelCodings |= g_rgdwHSCSDCodings[i];
            }
        }
    }
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_TRANSPCHANNELCODINGS;
#endif


    // Parse "<prefix>+CHSN: <aiur>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CHSN: ", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 8, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_HSCSDAIRRATES; i++)
    {
        if (nValue == g_rgdwHSCSDAirRates[i])
        {
            prhscsdi->dwAirInterfaceUserRate = i;
            break;
        }
    }
    if (NUM_HSCSDAIRRATES == i)
    {
        // We couldn't match the response with anything
        prhscsdi->dwAirInterfaceUserRate = RIL_HSCSDAIURATE_UNKNOWN;
    }
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_AIRINTERFACEUSERRATE;

    // Parse ",<rx_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prhscsdi->dwNonTranspRxTimeslots = nValue;
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_NONTRANSPRXTIMESLOTS;

    // Parse ",<top_rx_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prhscsdi->dwRxTimeslotsLimit = (nValue ? nValue : RIL_HSCSDTIMESLOTSLIMIT_NONE);
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_RXTIMESLOTSLIMIT;

    // Parse ",<codings><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Set all required channel coding bits
    if (!nValue)
    {
        prhscsdi->dwNonTranspChannelCodings = RIL_HSCSDCODING_ALL;
    }
    else
    {
        for (i = 0 ; i < NUM_HSCSDCODINGS; i++)
        {
            if (nValue & (0x01 << i))
            {
                prhscsdi->dwNonTranspChannelCodings |= g_rgdwHSCSDCodings[i];
            }
        }
    }
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_NONTRANSPCHANNELCODINGS;


    // Parse "+<prefix>CHSU: <mode><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "+CHSU: ", szRsp)          ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prhscsdi->fAutoSvcLevelUpgrading = !!nValue;
    prhscsdi->dwParams |= RIL_PARAM_HSCSDI_AUTOSVCLEVELUPGRADING;


    pBlob = (void*)prhscsdi;
    cbBlob = sizeof(RILHSCSDINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prhscsdi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetHSCSDOptions(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetHSCSDOptions);
#if !defined(WAVECOM_DRIVER) || defined(GSM_ATTEST)
    // HW-SPECIFIC: WaveCom hardware doesn't support AT+CHST, AT+CHSN, and AT+CHSU
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    // EMP does not support +CHST
    if (!QueueCmd(pHandle, "AT+CHSN?;+CHSU?\r", CMDOPT_NONE, APIID_GETHSCSDOPTIONS, ParseGetHSCSDOptions,
                  NULL, hr))
#else
    if (!QueueCmd(pHandle, "AT+CHST?;+CHSN?;+CHSU?\r", CMDOPT_NONE, APIID_GETHSCSDOPTIONS, ParseGetHSCSDOptions,
                  NULL, hr))
#endif
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}


//
//
//
HRESULT RILDrv_SetHSCSDOptions(DWORD dwParam, const RILHSCSDINFO* lpHscsdInfo)
{
    FUNCTION_TRACE(RILDrv_SetHSCSDOptions);
#if !defined(WAVECOM_DRIVER) || defined(GSM_ATTEST)
    // HW-SPECIFIC: WaveCom hardware doesn't support AT+CHST, AT+CHSN, and AT+CHSU

    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    UINT nValue;
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpHscsdInfo)
    {
        hr = E_FAIL;
        goto Error;
    }

#ifndef EMP_DRIVER
    // EMP does not support +CHST
    // Add "AT+CHST="
    (void)strncpyz(szWalk, "AT+CHST=", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<rx_timeslots>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_TRANSPRXTIMESLOTS)
    {
        if (RIL_HSCSDTIMESLOTS_DEFAULT == lpHscsdInfo->dwTranspRxTimeslots)
        {
            nValue = 0;
        }
        else
        {
            nValue = lpHscsdInfo->dwTranspRxTimeslots;
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<codings>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_TRANSPCHANNELCODINGS)
    {
        nValue = 0;
        if (RIL_HSCSDCODING_ALL != lpHscsdInfo->dwTranspChannelCodings)
        {
            for (i = 0 ; i < NUM_HSCSDCODINGS; i++)
            {
                if (lpHscsdInfo->dwTranspChannelCodings & g_rgdwHSCSDCodings[i])
                {
                    nValue += (0x01 << i);
                }
            }
            if (!nValue)
            {
                hr = E_INVALIDARG;
                goto Error;
            }
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }


    // Add ";+CHSN="
    (void)strncpyz(szWalk, ";+CHSN=", MAX_PATH - (szWalk - szCmd));
#else
    // Add "AT+CHSN="
    (void)strncpyz(szWalk, "AT+CHSN=", MAX_PATH - (szWalk - szCmd));
#endif

    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<auir>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_AIRINTERFACEUSERRATE)
    {
        if (RIL_HSCSDAIURATE_UNKNOWN == lpHscsdInfo->dwAirInterfaceUserRate)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        if (RIL_HSCSDAIURATE_DEFAULT == lpHscsdInfo->dwAirInterfaceUserRate)
        {
            nValue = 0;
        }
        else
        {
            DEBUGCHK(RIL_HSCSDAIURATE_57600 >= lpHscsdInfo->dwAirInterfaceUserRate);
            nValue = g_rgdwHSCSDAirRates[lpHscsdInfo->dwAirInterfaceUserRate];
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<rx_timeslots>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_NONTRANSPRXTIMESLOTS)
    {
#ifdef EMP_DRIVER
        // EMP only supports 0, 1 and 2 for <rx_timeslots>
        if (3 <= lpHscsdInfo->dwNonTranspRxTimeslots)
        {
            hr = E_NOTIMPL;
            goto Error;
        }
#endif
        if (RIL_HSCSDTIMESLOTS_DEFAULT == lpHscsdInfo->dwNonTranspRxTimeslots)
        {
            nValue = 0;
        }
        else
        {
            nValue = lpHscsdInfo->dwNonTranspRxTimeslots;
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<top_rx_timeslots>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_RXTIMESLOTSLIMIT)
    {
#ifdef EMP_DRIVER
        // EMP only supports 0, 1 and 2 for <top_rx_timeslots>
        if (3 <= lpHscsdInfo->dwRxTimeslotsLimit)
        {
            hr = E_NOTIMPL;
            goto Error;
        }
#endif
        if (RIL_HSCSDTIMESLOTSLIMIT_NONE == lpHscsdInfo->dwRxTimeslotsLimit)
        {
            nValue = 0;
        }
        else
        {
            nValue = lpHscsdInfo->dwRxTimeslotsLimit;
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<codings>"
    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_NONTRANSPCHANNELCODINGS)
    {
        nValue = 0;
        if (RIL_HSCSDCODING_ALL != lpHscsdInfo->dwNonTranspChannelCodings)
        {
            for (i = 0 ; i < NUM_HSCSDCODINGS; i++)
            {
                if (lpHscsdInfo->dwNonTranspChannelCodings & g_rgdwHSCSDCodings[i])
                {
                    nValue += (0x01 << i);
                }
            }
            if (!nValue)
            {
                hr = E_INVALIDARG;
                goto Error;
            }
#ifdef EMP_DRIVER
            // EMP only supports (0,4,8,12) for <codings>
            if ((0 != nValue) && (4 != nValue) && (8 != nValue) && (12 != nValue))
            {
                hr = E_NOTIMPL;
                goto Error;
            }
#endif
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    if (lpHscsdInfo->dwParams & RIL_PARAM_HSCSDI_AUTOSVCLEVELUPGRADING)
    {
        // Add ";+CHSU=<mode>"
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), ";+CHSU=%u",
                         (lpHscsdInfo->fAutoSvcLevelUpgrading ? 1 : 0));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETHSCSDOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}
//
//
//
static HRESULT ParseGetHSCSDCallSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetHSCSDCallSettings);
    UINT nValue;
    UINT i;
    RILCALLHSCSDINFO* prchscsdi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prchscsdi = (RILCALLHSCSDINFO*)AllocBlob(sizeof(RILCALLHSCSDINFO));
    if (!prchscsdi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prchscsdi, 0x00, sizeof(RILCALLHSCSDINFO));
    prchscsdi->cbSize = sizeof(RILCALLHSCSDINFO);

    // Parse "<prefix>+CHSC: <rx_timeslots>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CHSC: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prchscsdi->dwRxTimeslots = nValue;
    prchscsdi->dwParams |= RIL_PARAM_CHSCSDI_RXTIMESLOTS;

    // Parse ",<tx_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    prchscsdi->dwTxTimeslots = nValue;
    prchscsdi->dwParams |= RIL_PARAM_CHSCSDI_TXTIMESLOTS;

    // Parse ",<aiur>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_HSCSDAIRRATES; i++)
    {
        if (nValue == g_rgdwHSCSDAirRates[i])
        {
            prchscsdi->dwAirInterfaceUserRate = i;
            break;
        }
    }
    if (NUM_HSCSDAIRRATES == i)
    {
        // We couldn't match the response with anything
        prchscsdi->dwAirInterfaceUserRate = RIL_HSCSDAIURATE_UNKNOWN;
    }
    prchscsdi->dwParams |= RIL_PARAM_CHSCSDI_AIRINTERFACEUSERRATE;

    // Parse ",<coding><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0 ; i < NUM_HSCSDCODINGS; i++)
    {
        if ((0x01 << i) == (int)nValue)
        {
            prchscsdi->dwChannelCoding = g_rgdwHSCSDCodings[i];
            break;
        }
    }
    if (NUM_HSCSDCODINGS == i)
    {
        prchscsdi->dwChannelCoding = RIL_HSCSDCODING_UNKNOWN;
    }
    prchscsdi->dwParams |= RIL_PARAM_CHSCSDI_CHANNELCODING;

    pBlob = (void*)prchscsdi;
    cbBlob = sizeof(RILCALLHSCSDINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prchscsdi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetHSCSDCallSettings(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetHSCSDCallSettings);
#if !defined(WAVECOM_DRIVER) || defined(GSM_ATTEST)
    // HW-SPECIFIC: WaveCom hardware doesn't support AT+CHSC

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CHSC\r", CMDOPT_NONE, APIID_GETHSCSDCALLSETTINGS, ParseGetHSCSDCallSettings, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetBearerServiceOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetBearerServiceOptions);
    UINT nValue;
    UINT i;
    RILBEARERSVCINFO* prbsi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prbsi = (RILBEARERSVCINFO*)AllocBlob(sizeof(RILBEARERSVCINFO));
    if (!prbsi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prbsi, 0x00, sizeof(RILBEARERSVCINFO));
    prbsi->cbSize = sizeof(RILBEARERSVCINFO);

    // Parse "<prefix>+CBST: <speed>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CBST: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_CONNSPEEDS; i++)
    {
        if (nValue == g_rgdwConnSpeeds[i])
        {
            prbsi->dwSpeed = i;
            break;
        }
    }
    if (NUM_CONNSPEEDS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwSpeed = RIL_SPEED_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SPEED;

    // Parse ",<svc_name>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCNAMES; i++)
    {
        if (nValue == g_rgdwBearerSvcNames[i])
        {
            prbsi->dwServiceName = i;
            break;
        }
    }
    if (NUM_BEARERSVCNAMES == i)
    {
        // We couldn't match the response with anything
        prbsi->dwServiceName = RIL_BSVCNAME_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SERVICENAME;

    // Parse ",<conn_element><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCCONNELEMS; i++)
    {
        if (nValue == g_rgdwBearerSvcConnElems[i])
        {
            prbsi->dwConnectionElement = i;
            break;
        }
    }
    if (NUM_BEARERSVCCONNELEMS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwConnectionElement = RIL_BSVCCE_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_CONNECTIONELEMENT;

    pBlob = (void*)prbsi;
    cbBlob = sizeof(RILBEARERSVCINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prbsi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetBearerServiceOptions(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetBearerServiceOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CBST?\r", CMDOPT_NONE, APIID_GETBEARERSERVICEOPTIONS, ParseGetBearerServiceOptions,
                  NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetBearerServiceOptions(DWORD dwParam, const RILBEARERSVCINFO* lpBearerServiceInfo)
{
    FUNCTION_TRACE(RILDrv_SetBearerServiceOptions);
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
#ifdef APP_CSD_SETTING
    DWORD dwBaudrate=-1;
    DWORD dwConelemt=-1;
#endif  
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpBearerServiceInfo)
    {
        hr = E_FAIL;
        goto Error;
    }
#ifdef APP_CSD_SETTING
    //read baud rate for CSD 
    if (!GetRegistryDWORD(HKEY_CURRENT_USER, g_tszRegKeyCsdSetting, TEXT("CSDdatarate"), &dwBaudrate))
    {
        dwBaudrate = 7; //set RIL_SPEED_9600_V32 as default
    }
    //read connection element for CSD
    if (!GetRegistryDWORD(HKEY_CURRENT_USER, g_tszRegKeyCsdSetting, TEXT("CSDconelemt"), &dwConelemt))
    {
        dwConelemt = 1; //set NON_TRANSPARENT as default, customer requirement
    }
#endif
    // Add "AT+CBST="
    (void)strncpyz(szWalk, "AT+CBST=", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<speed>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SPEED)
    {
        DEBUGCHK(RIL_SPEED_64000_MULTIMEDIA >= lpBearerServiceInfo->dwSpeed);

        if (RIL_SPEED_UNKNOWN == lpBearerServiceInfo->dwSpeed)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

#ifdef EMP_DRIVER
        UINT nValue = g_rgdwConnSpeeds[lpBearerServiceInfo->dwSpeed];
        // EMP only supports (0,7,12,14-17,39,43,47-51,71,75,79-84) for <speed>
        if (( 0 != nValue) && ( 7 != nValue) && (12 != nValue) && (14 != nValue) && (15 != nValue) &&
            (16 != nValue) && (17 != nValue) && (39 != nValue) && (43 != nValue) && (47 != nValue) &&
            (48 != nValue) && (49 != nValue) && (50 != nValue) && (51 != nValue) && (71 != nValue) &&
            (75 != nValue) && (79 != nValue) && (80 != nValue) && (81 != nValue) && (82 != nValue) &&
            (83 != nValue) && (84 != nValue))
        {
            hr = E_NOTIMPL;
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
#else

#ifdef APP_CSD_SETTING
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", dwBaudrate);
#else
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", g_rgdwConnSpeeds[lpBearerServiceInfo->dwSpeed]);
#endif

#endif
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<svc_name>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SERVICENAME)
    {
        DEBUGCHK(RIL_BSVCNAME_PACKETACCESS_SYNC_RDI >= lpBearerServiceInfo->dwServiceName);

        if (RIL_BSVCNAME_UNKNOWN == lpBearerServiceInfo->dwServiceName)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

#ifdef EMP_DRIVER
        // EMP only supports the following two service names
        if (RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM != lpBearerServiceInfo->dwServiceName &&
            RIL_BSVCNAME_DATACIRCUIT_ASYNC_RDI != lpBearerServiceInfo->dwServiceName)
        {
            hr = E_NOTIMPL;
            goto Error;
        }
#endif

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", g_rgdwBearerSvcNames[lpBearerServiceInfo->dwServiceName]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<conn_elem>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_CONNECTIONELEMENT)
    {
        DEBUGCHK(RIL_BSVCCE_BOTH_NONTRANSPARENT >= lpBearerServiceInfo->dwConnectionElement);

        if (RIL_BSVCCE_UNKNOWN == lpBearerServiceInfo->dwConnectionElement)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

#ifdef EMP_DRIVER
        // EMP only supports non-transparent mode
        if (RIL_BSVCCE_NONTRANSPARENT != lpBearerServiceInfo->dwConnectionElement)
        {
            hr = E_NOTIMPL;
            goto Error;
        }
#endif

#ifdef APP_CSD_SETTING
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", dwConelemt);
#else
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",
                         g_rgdwBearerSvcConnElems[lpBearerServiceInfo->dwConnectionElement]);
#endif
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

#ifdef EMP_DRIVER
    // EMP must send +CHSN before dialing CS data call
    // This is a temporary solution as higher layer does not call RIL API to configure +CHSN
    // 1,1,0,4 is the non-transparent call configuration for the China mobile environment
    // OEM should set appropriate non-transparent call configuration for target environment
    (void)strncpyz(szWalk, ";+CHSN=1,1,0,4", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');
#endif
    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETBEARERSERVICEOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
static HRESULT ParseGetRLPOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetRLPOptions);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILRLPINFO* rgrrlpi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CRLP: "
    while (MatchStringBeginning(szRsp, "+CRLP: ", szRsp))
    {
        if (nUsed == nAllocated)
        {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrrlpi, sizeof(RILRLPINFO), nUsed, &nAllocated, DATA_ALLOC_NUMBER))
            {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrrlpi[nUsed], 0x00, sizeof(RILRLPINFO));
        rgrrlpi[nUsed].cbSize = sizeof(RILRLPINFO);

        // Parse "<iws>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwIWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_IWS;

        // Parse ",<mws>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwMWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_MWS;

        // Parse ",<ack_timer>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwAckTimer = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_ACKTIMER;

        // Parse ",<retr_att>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwRetransmissionAttempts = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp))
        {
            // Parse "<version>"
            if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
            {
                goto Continue;
            }
            rgrrlpi[nUsed].dwVersion = nValue;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp))
            {
                // Parse "<reseq_period>"
                if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
                {
                    goto Continue;
                }
                rgrrlpi[nUsed].dwResequencingPeriod = nValue;
                rgrrlpi[nUsed].dwParams |= RIL_PARAM_RPLI_RESEQUENCINGPERIOD;
            }
        }

        // If we didn't see the version, assume 0 (per GSM 07.07, ch6.8)
        if (!(rgrrlpi[nUsed].dwParams & RIL_PARAM_RLPI_VERSION))
        {
            rgrrlpi[nUsed].dwVersion = 0;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;
        }

        // Increment the array index
        nUsed++;

        Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrrlpi;
    cbBlob = nUsed * sizeof(RILRLPINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(rgrrlpi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetRLPOptions(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetRLPOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CRLP?\r", CMDOPT_NONE, APIID_GETRLPOPTIONS, ParseGetRLPOptions, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetRLPOptions(DWORD dwParam, const RILRLPINFO* lpRlpInfo)
{
    FUNCTION_TRACE(RILDrv_SetRLPOptions);
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpRlpInfo)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+CRLP="
    (void)strncpyz(szWalk, "AT+CRLP=", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<iws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_IWS)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwIWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<mws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_MWS)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwMWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<ack_timer>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_ACKTIMER)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwAckTimer);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<retr_att>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwRetransmissionAttempts);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Only include version info if version is 2 or above
    if ((lpRlpInfo->dwParams & RIL_PARAM_RLPI_VERSION) && (lpRlpInfo->dwVersion>=2))
    {
        // Add ","
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        // Add "<version>"
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwVersion);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        if (lpRlpInfo->dwParams & RIL_PARAM_RPLI_RESEQUENCINGPERIOD)
        {
            // Add ","
            (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);

            // Add "<reseq_period>"
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwResequencingPeriod);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETRLPOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +DS: <dir>,<neg>,<P1>,<P2>
static HRESULT ParseGetDataCompression(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDataCompression);
    UINT nValue;
    RILDATACOMPINFO* prdci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prdci = (RILDATACOMPINFO*)AllocBlob(sizeof(RILDATACOMPINFO));
    if (!prdci)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prdci, 0x00, sizeof(RILDATACOMPINFO));
    prdci->cbSize = sizeof(RILDATACOMPINFO);

    // Parse "<prefix>+DS: <dir>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "+DS: ", szRsp)    ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (FlagFromValue(nValue, g_DataCompDirection, NUM_DATACOMPDIRECTION, prdci->dwDirection))
    {
        prdci->dwParams |= RIL_PARAM_DCI_DIRECTION;
    }

    // Parse ",<neg>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (FlagFromValue(nValue, g_DataCompNegotiation, NUM_DATACOMPNEGOTIATION, prdci->dwNegotiation))
    {
        prdci->dwParams |= RIL_PARAM_DCI_NEGOTIATION;
    }

    // Parse ",<P1>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    prdci->dwMaxDictEntries = nValue;
    prdci->dwParams |= RIL_PARAM_DCI_MAXDICTENTRIES;

    // Parse ",<P2><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp)         ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    prdci->dwMaxStringLength = nValue;
    prdci->dwParams |= RIL_PARAM_DCI_MAXSTRING;

    pBlob = (void*)prdci;
    cbBlob = sizeof(RILDATACOMPINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prdci);
    }
    return hr;
}

HRESULT RILDrv_GetDataCompression (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetDataCompression);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+DS?\r", CMDOPT_NONE, APIID_GETDATACOMPRESSION, ParseGetDataCompression, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +DS=[<dir>[,<neg>[,<P1>[,<P2>]]]]
HRESULT RILDrv_SetDataCompression (DWORD dwParam, const RILDATACOMPINFO* lpDataCompInfo)
{
    FUNCTION_TRACE(RILDrv_SetDataCompression);
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    UINT nValue;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpDataCompInfo)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+DS="
    (void)strncpyz(szWalk, "AT+DS=", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<dir>"
    if (lpDataCompInfo->dwParams & RIL_PARAM_DCI_DIRECTION)
    {
        if (!ValueFromFlag(lpDataCompInfo->dwDirection, g_DataCompDirection, NUM_DATACOMPDIRECTION, nValue))
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<neg>"
    if (lpDataCompInfo->dwParams & RIL_PARAM_DCI_NEGOTIATION)
    {
        if (!ValueFromFlag(lpDataCompInfo->dwNegotiation, g_DataCompNegotiation, NUM_DATACOMPNEGOTIATION, nValue))
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<P1>"
    if (lpDataCompInfo->dwParams & RIL_PARAM_DCI_MAXDICTENTRIES)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpDataCompInfo->dwMaxDictEntries);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add "<P2>"
    if (lpDataCompInfo->dwParams & RIL_PARAM_DCI_MAXSTRING)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpDataCompInfo->dwMaxStringLength);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETDATACOMPRESSION, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +ES: <orig_rqst>,<orig_fbk>,<ans_fbk>
static HRESULT ParseGetErrorCorrection(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetErrorCorrection);
    UINT nValue;
    RILERRORCORRECTIONINFO* preci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    preci = (RILERRORCORRECTIONINFO*)AllocBlob(sizeof(RILERRORCORRECTIONINFO));
    if (!preci)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(preci, 0x00, sizeof(RILERRORCORRECTIONINFO));
    preci->cbSize = sizeof(RILERRORCORRECTIONINFO);

    // Parse "<prefix>+ES: <orig_rqst>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "+ES: ", szRsp)    ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    FlagFromValue(nValue, g_ErrorCorrectionOrigRqst, NUM_ERRORCORRECTIONORIGRQST, preci->dwOriginalRequest);

    // Parse ",<orig_fbk>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    FlagFromValue(nValue, g_ErrorCorrectionOrigFbk, NUM_ERRORCORRECTIONORIGFBK, preci->dwOriginalFallback);

    // Parse ",<ans_fbk><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, FALSE, nValue, szRsp)         ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    FlagFromValue(nValue, g_ErrorCorrectionAnsFbk, NUM_ERRORCORRECTIONANSFBK, preci->dwAnswererFallback);
    preci->dwParams |= RIL_PARAM_ECI_ALL;

    pBlob = (void*)preci;
    cbBlob = sizeof(RILERRORCORRECTIONINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(preci);
    }
    return hr;
}

HRESULT RILDrv_GetErrorCorrection (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetErrorCorrection);
    HRESULT hr = S_OK;
#ifdef EMP_DRIVER
    hr = E_NOTIMPL;
    goto Error;
#endif
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+ES?\r", CMDOPT_NONE, APIID_GETERRORCORRECTION, ParseGetErrorCorrection, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +ES=[<orig_rqst>[,<orig_fbk>[,<ans_fbk>]]]
HRESULT RILDrv_SetErrorCorrection (DWORD dwParam, const RILERRORCORRECTIONINFO* lpErrorCorrectionInfo)
{
    FUNCTION_TRACE(RILDrv_SetErrorCorrection);
    char szCmd[MAX_PATH];
    UINT nValue;
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
#ifdef EMP_DRIVER
    hr = E_NOTIMPL;
    goto Error;
#endif
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpErrorCorrectionInfo)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+ES="
    (void)strncpyz(szWalk, "AT+ES=", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (!ValueFromFlag(lpErrorCorrectionInfo->dwOriginalRequest, g_ErrorCorrectionOrigRqst, NUM_ERRORCORRECTIONORIGRQST, nValue))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Add "<orig_rqst>"
    if (lpErrorCorrectionInfo->dwParams & RIL_PARAM_ECI_ORIGINALREQUEST)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (!ValueFromFlag(lpErrorCorrectionInfo->dwOriginalFallback, g_ErrorCorrectionOrigFbk, NUM_ERRORCORRECTIONORIGFBK, nValue))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Add "<orig_fbk>"
    if (lpErrorCorrectionInfo->dwParams & RIL_PARAM_ECI_ORIGINALFALLBACK)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (!ValueFromFlag(lpErrorCorrectionInfo->dwAnswererFallback, g_ErrorCorrectionAnsFbk, NUM_ERRORCORRECTIONANSFBK, nValue))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Add "<ans_fbk>"
    if (lpErrorCorrectionInfo->dwParams & RIL_PARAM_ECI_ANSWERERFALLBACK)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETERRORCORRECTION, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT ParseGetDevCapsDataCompression(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsDataCompression);

    RILCAPSDATACOMPRESSION *prcdc = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcdc = (RILCAPSDATACOMPRESSION*)AllocBlob(sizeof(RILCAPSDATACOMPRESSION));
    if (!prcdc)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcdc, 0, sizeof(RILCAPSDATACOMPRESSION));
    prcdc->cbSize = sizeof(RILCAPSDATACOMPRESSION);

#ifdef EMP_DRIVER
    // Response on EMP platform is +DS: (0-3),(0-1),(512,1024,1536,2048),(6-250)<cr><lf>
    const DWORD dwMaxRanges=10;
    RILRANGE rgrrRange[dwMaxRanges];
    DWORD dwNumRanges=0;
#endif
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+DS: ", szRsp) ||
        !ParseRangeList(szRsp, g_DataCompDirection, NUM_DATACOMPDIRECTION, prcdc->dwDirection, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRangeList(szRsp, g_DataCompNegotiation, NUM_DATACOMPNEGOTIATION, prcdc->dwNegotiation, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
#ifdef EMP_DRIVER
        !MatchStringBeginning(szRsp, "(", szRsp) ||
        !ParseRangesFromString(szRsp, rgrrRange, dwNumRanges, dwMaxRanges, szRsp) ||
        !MatchStringBeginning(szRsp, ")", szRsp) ||
#else
        !ParseRange(szRsp, prcdc->rrMaxDict, szRsp) ||
#endif
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRange(szRsp, prcdc->rrMaxString, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
#ifdef EMP_DRIVER
    if (dwNumRanges<1)
    {
        hr = E_FAIL;
        goto Error;
    }
    // Get the minimal and maximal values from the list
    prcdc->rrMaxDict.dwMinValue = rgrrRange[0].dwMinValue;
    prcdc->rrMaxDict.dwMaxValue = rgrrRange[dwNumRanges-1].dwMaxValue;
#endif

    prcdc->dwParams = RIL_PARAM_CDC_ALL;

    pBlob = (void*)prcdc;
    cbBlob = sizeof(RILCAPSDATACOMPRESSION);

Error:
    if (FAILED(hr))
    {
        FreeBlob(prcdc);
    }

    return hr;
}

//
//
//
HRESULT ParseGetDevCapsErrorCorrection(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsErrorCorrection);

    RILCAPSERRORCORRECTION *prcec = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcec = (RILCAPSERRORCORRECTION*)AllocBlob(sizeof(RILCAPSERRORCORRECTION));
    if (!prcec)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcec, 0, sizeof(RILCAPSERRORCORRECTION));
    prcec->cbSize = sizeof(RILCAPSERRORCORRECTION);

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+ES: ", szRsp) ||
        !ParseRangeList(szRsp, g_ErrorCorrectionOrigRqst, NUM_ERRORCORRECTIONORIGRQST, prcec->dwOriginalRequest, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRangeList(szRsp, g_ErrorCorrectionOrigFbk, NUM_ERRORCORRECTIONORIGFBK, prcec->dwOriginalFallback, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRangeList(szRsp, g_ErrorCorrectionAnsFbk, NUM_ERRORCORRECTIONANSFBK, prcec->dwAnswererFallback, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    prcec->dwParams = RIL_PARAM_CDC_ALL;

    pBlob = (void*)prcec;
    cbBlob = sizeof(RILCAPSERRORCORRECTION);

Error:
    if (FAILED(hr))
    {
        FreeBlob(prcec);
    }

    return hr;
}

HRESULT RILDrv_SetATLogging(DWORD dwParam, BOOL fEnable)
{
   FUNCTION_TRACE(RILDrv_SetATLogging);
   HRESULT hr = S_OK;
   CRilHandle* pRilDevice = NULL;
  
   CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
   if (!pHandle)
   {
     hr = E_FAIL;
     goto Error;
   }
    
   pRilDevice = pHandle->GetDevice();
   if ( pRilDevice ) 
   {
     if ( fEnable )
       pRilDevice->SetAtLogOwner( pHandle );
     else
       pRilDevice->SetAtLogOwner(0);
   }

   // Build command
   if (!QueueCmd(pHandle,
                  NULL,
                  CMDOPT_NOOP,
                  APIID_REGISTERATCOMMANDLOGGING,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
        hr=E_FAIL;

   Error:
    return hr;
}


// For Microsoft Test only. This does not need to be fully implemented.
// A return response of E_NOTIMPL is appropriate. 
HRESULT RILDrv_ATLogFile(DWORD dwParam, LPTSTR pszFilename)
{
    FUNCTION_TRACE(RILDrv_ATLogFile);
    HRESULT hr = S_OK;
    CRilHandle* pRilDevice = NULL;


    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !pszFilename)
    {
        hr = E_FAIL;
        goto Error;
    }

// If Watson support is not enabled return not implemented.
#ifndef RIL_WATSON_REPORT
    hr = E_NOTIMPL;
    goto Error;
    
#else
    // Move the read point in the at log buffer to the beginning
    // of the first command. (This removes partial log entries.)
    g_RilATCmdLogBuffer.AdvanceReadPastDelimiter((BYTE)'\n');
    
    // Create the log file.
    if ( !(g_RilATCmdLogBuffer.DumpToFile(pszFilename)) )
    {
         hr = E_FAIL;
         goto Error;        
    }
    
    // Build command
    if (!QueueCmd(pHandle,
                  NULL,
                  CMDOPT_NOOP,
                  APIID_ATCOMMANDLOGFILE,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr=E_FAIL;
    }
#endif // RIL_WATSON_REPORT

    
Error:
    return hr;
}

HRESULT RILDrv_GetSerialPortHandleFromContextID(DWORD dwContextID, TCHAR *tszDataPortName, DWORD dwSize, DWORD *pdwSizeOut)
{
    HRESULT hr = E_FAIL;

#ifdef EMP_DRIVER
    // EMP supports multiple context ID
    if (dwContextID > 0)
    {
        // Look up the VSP name in the mapping table for the specified <cid>
        CCidSerialPortHandleMap* pCidHandleMap = CCidSerialPortHandleMap::Instance();
        if ( NULL!=pCidHandleMap )
        {
            hr = pCidHandleMap->GetSerialPortNameFromContextID(dwContextID, tszDataPortName, dwSize, pdwSizeOut);
        }
    }
#else
    if ( tszDataPortName && pdwSizeOut )
    {
        // At the time of this implementation, we do not have any vendors that support multiple PPP-based APNs. In this case,
        // we will simply do what the proxy currently does. This allows for basic testing.
        *pdwSizeOut = 0;
        if ( dwSize > 6 )
        {
            // Get the COM port name
            if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL"), TEXT("DataPort"), tszDataPortName, 6))
            {
                _tcsncpyz(tszDataPortName, TEXT("COM9:"), 6);
            }
            *pdwSizeOut = _tcslen ( tszDataPortName );
            hr = S_OK;
        }
    }
#endif

    return hr;
}

HRESULT RILDrv_GetVTSerialPortHandle(TCHAR *tszVTPortName, DWORD dwSize, DWORD *pdwSizeOut)
{
    HRESULT hr = E_NOTIMPL;

#ifdef EMP_DRIVER
    hr = E_INVALIDARG;
    if ( tszVTPortName && pdwSizeOut )
    {
        *pdwSizeOut = 0;
        if ( dwSize > 6 )
        {
            // VEI6 is used for VT
            _tcsncpyz(tszVTPortName, TEXT("VEI6:"), 6);
            *pdwSizeOut = _tcslen(tszVTPortName);
            hr = S_OK;
        }
    }
#endif

    return hr;
}
